package com.cantor.common.util;

import com.cantor.common.serializer.ToBeSerializedData;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

/**
 * Protobuf序列化工具类
 */
public class ProtostuffUtil {

    // 线程安全的获取buffer, 默认512
    private static final ThreadLocal<LinkedBuffer> BUFFER_THREAD_LOCAL = ThreadLocal
            .withInitial(() -> LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));

    /**
     * @desc 序列化
     * @auth wulm
     */
    @SuppressWarnings("unchecked")
    public static <T> byte[] serialize(T obj) {
        ToBeSerializedData<T> data = new ToBeSerializedData<>(obj);
        data.setTarget(obj);

        // this is lazily created and cached by RuntimeSchema
        // so its safe to call RuntimeSchema.getSchema(Foo.class) over and over
        // The getSchema method is also thread-safe
        Schema<ToBeSerializedData> schema = RuntimeSchema.getSchema((Class<ToBeSerializedData>) data.getClass());

        // Re-use (manage) this buffer to avoid allocating on every serialization
        // LinkedBuffer buffer = LinkedBuffer.allocate(512);
        LinkedBuffer buffer = BUFFER_THREAD_LOCAL.get();

        // ser
        try {
            return ProtostuffIOUtil.toByteArray(data, schema, buffer);
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }

    }

    /**
     * @desc 反序列化
     * @auth wulm
     */
    @SuppressWarnings("unchecked")
    public static <T> T deserialize(byte[] data, Class<T> cls) {
        Schema<ToBeSerializedData> schema = RuntimeSchema.getSchema(ToBeSerializedData.class);
        // deser
        ToBeSerializedData message = schema.newMessage();
        ProtostuffIOUtil.mergeFrom(data, message, schema);
        return (T) message.getTarget();
    }


}
